Pytigon rozszerza o dodatkowe funkcjonalności klasę służącą do definiowana modeli danych biblioteki django: django.db.models.Model. Klasa "JSONModel" poza tym, że posiada dodatkowe pole "jsondata" definiuje dodatkowe funkcje, które ułatwiają tworzenie modeli danych.
class JSONModel(models.Model):
jsondata = models.JSONField(
"Json data",
null=True,
blank=True,
editable=False,
)
...
Dodatkowe funkcje to:
Django potrafi utworzyć formularz dla models.Model (patrz django.forms.ModelForm)
W przypadku pola jsondata domyślny formularz nie zawsze jest rozwiązaniem optymalnym.
JSONModel pozwala na zdefiniowanie w modelach dziedziczących własnego formularza w sposób dynamiczny.
Wystarczy aby model potomny definiował funkcję get_form_source, która zwróci w postaci tekstowej definicję formularza -
skladnia musi być akceptowana przez funkcję: pytigon_lib.schdjangoext.fastform.form_from_str
Za pomocą tej funkcji możesz przedefiniować w obiekcie potomnym atrybut pola w modelu nadrzędnym. Funckcja zwraca zmienione pole, które możesz dalej zmieniać w sposób bezpośredni.
np:
field = self.set_field_value("item", "blank", False)
field.verbose_name = _("New description")
do sprawdzenia:
field = self.set_field_value("grand_parent1", "limit_choices_to", {'type': 'I-DEV'},)
Jeżeli chcesz wyświetlić tabelę skojarząną z wierszem innej tabel musisz zdefiniować połączenie przy pomocy dwóch dodatkowych metod w tabeli wiązanej, są to metody filter oraz init_new. Najlepiej pokazać to na przykładzie tabeli występującej w projekcie _schtools: schattachements.models.Attachements.
@classmethod
def filter(cls, value):
if value:
app, tbl, id, grp = value.split('__')
return cls.objects.filter(application=app, table=tbl, parent_id=id, group=grp)
else:
return cls.objects.all()
def init_new(self, request, view, value=None):
if value:
app, tbl, id, grp = value.split('__')
return { 'application': app, 'table': tbl, 'parent_id': id, 'group': grp }
else:
return { 'application': 'default', 'table': 'default', 'parent_id': 0, 'group': 'default' }
Pierwsza metoda filtruje wiersze na podstawie wartości value. Pozwala to na dodanie następującego powiązania w szablonie innej tabeli:
Zmienna filter przekazywana jest z tag'u row_related_list do metody filter. Na jej podstawie filtrowane są aplikacje
Funkcja init_new na podstawie aktualnie ustawionego filtru inicjuje nowy obiekt. Funkcję tą uzupełnia tag: new_row, któremu należy przedefiniować parametr action tak jak w przykładzie poniżej:
Jeżeli zdefiniujesz tą funkcję będzie ona automatycznie wywoływana przez Pytigon Generic Views zamiast domyślnej metody model.save(). Pozwoli ci to na dostęp do danych powiązanych z request'em.
Pytigon umożliwia ci na zamianę danego obiektu na obiekt dziedziczący go
example:
def get_derived_object(self, param=None):
t = None
if type(self) == DocHead:
if param and "view" in param and "add_param" in param["view"].kwargs:
t = param["view"].kwargs["add_param"]
if t == "-":
return self
object_list = DocType.objects.filter(name=t)
if len(object_list):
t = object_list[0].parent.name
return ContentType.objects.get(
model=t.lower() + "dochead"
).model_class()()
else:
t = self.doc_type_parent.parent.name
name = t.lower() + "dochead"
if hasattr(self, name):
return getattr(self, name)
return self
example:
from pytigon_lib.schviews import actions
...
@classmethod
def table_action(cls, list_view, request, data):
if "action" in data:
if data["action"] == "insert_rows":
table = data["table"]
...
return actions.refresh(request)
return standard_table_action(cls, list_view, request, data, ["copy", "paste"])
@staticmethod
def filter_by_permissions(queryset_or_obj, request):
if queryset_or_obj != None:
print(queryset_or_obj.model)
if queryset_or_obj.model == SchoolElement:
return schelements_models.Element.objects.filter(
type__in=schelements_models.Element.get_structure().keys()
)
return queryset_or_obj
sort: request.GET.get("sort") (sort columns)
order: request.GET.get("order") - 'desc' or 'asc'
Użyj tej funkcji, jeżeli chcesz przekierować request na nowy href.
example:
def redirect_href(self, view, request):
t = None
if type(self)==Element:
if 'add_param' in view.kwargs:
t = view.kwargs['add_param']
else:
t = self.type
if t:
if hasattr(self, "get_structure"):
s = self.get_structure()
if t in s:
redirect = s[t]['app'] + "/table/" + s[t]['table']
return request.path.replace('schelements/table/Element',redirect)
return None
example:
def get_form_class(self, view, request, create):
base_form = view.get_form_class()
object_list = schelements_models.Element.get_children_for_element("C", "O-DIV")
choices = [(object.pk, object.name) for object in object_list]
if self.parent_element:
pk = self.parent_element.id
else:
pk = None
class _Form(base_form):
parent_element_id = forms.ChoiceField(
label="Class",
choices=choices,
initial=pk,
widget=forms.Select(),
required=True,
)
return _Form
Jeżeli potrzebujesz zweryfikować formularz bazujący na wierszu tabeli zdefiniuj tą funkcję. Jeżeli funkcja jest zdefiniowana zastępuje ona wbudowaną funkcję form.is_valis.
example:
@staticmethod
def is_form_valid(form):
if form.is_valid():
x = form.cleaned_data['field1']
if not x:
form.add_error(None, 'description')
return False
else:
return True
else:
return False
example:
def post_form(self, view, form, request):
pk = form.cleaned_data["parent_element_id"]
if pk:
self.parent_element = schelements_models.Element.objects.get(pk=pk)
return True
Jeżeli chcesz zdefiniować template w zależności od wybranego wiersza tabeli, użyj tej metody. Powinna ona zwrócić nazwę szablonu. Jeżeli funckja zwróci None, użyty zostanie domyślny szablon.
example:
def template_for_object(self, view, context, doc_type):
if doc_type == "pdf":
x = ReportDef.objects.filter(name=self.report_def_name)
if x.count() > 0:
return "%s/report_%s_pdf.html" % (x[0].app, self.report_def_name)
return None
Jeżeli chcesz zdefiniować template dla tabeli, użyj tej metody. Funcka pozwala na dynamiczną zmianę domyślnego szablonu, wyboru można dokonać bazując na przekazanych parametrach.
@staticmethod
def template_for_list(view, model, context, doc_type):
if doc_type in ("html", "json") and "filter" in context:
tmp = DocReg.objects.filter(name=context["filter"].replace("_", "/"))
if len(tmp) == 1:
names = []
x = tmp[0]
while x:
names.append(
(
x.app
+ "/"
+ x.name.replace("/", "_")
+ "_dochead_list.html"
).lower()
)
x = x.get_parent()
if "target" in view.kwargs and "calendar" in view.kwargs["target"]:
names2 = []
for name in names:
names2.append(
name.replace(".html", "_" + view.kwargs["target"] + ".html")
)
names = names2
template = select_template(names)
names.append(view.template_name)
if template:
return template
return None